/**
 * Copyright (c) 2009, Nathan Bubna
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * This plugin depends upon the Values plugin to provide a set of "sync"
 * methods that can help you keep values in various parts of the page
 * automatically synchronized.
 *
 * @version 0.2
 * @name values
 * @cat Plugins/Sync
 * @author Nathan Bubna
 */
(function ($) {

    // change some values defaults
    $.values.defaults.setEvents = true;

    var S = $.sync = {
        version: "0.2",
        defaults: {
            bidi: true,
            bind: {
                select: 'change',
                textarea: 'keyup',
                input: {
                    text: 'keyup',
                    submit: 'click',
                    image: 'click',
                    radio: 'click',
                    checkbox: 'click'
                }
            }
        },
        sync: function($target, $source, options) {
            var $fields = 
            $source.bind('set.values', function(e, key) {
                var block = 'sync|'+key;
                if (!$target.data(block)) {
                    var $source = $(this).data(block, true),
                        value = $source.values(key);
                    if (value === undefined) {
                        value = null;
                    }
                    //log(block, '=',value,'from',this,'to',$target);
                    $target.data(block, true)
                           .values(key, value, options)
                           .data(block, false);
                    $source.data(block, false);
                }
            }).find('select,input,textarea');
            $source.each(function() {
                if (options.bind[this.nodeName.toLowerCase()]) {
                    $fields = $fields.add(this);
                }
            });
            $fields.each(function() {
                S.watch.call(this, options);
            });
        },
        type: function(elem, options) {
            var t = options.bind[elem.nodeName.toLowerCase()];
            if (t === null || t === undefined) {
                return 'change';
            }
            if (typeof t == "object") {
                return t[$(elem).attr('type')];
            }
            return t;
        },
        watch: function(options) {
            var $field = $(this),
                triggers = $field.data('sync.triggers'),
                type = S.type(this, options),
                bind = true;
            if (triggers === null || triggers === undefined) {
                triggers = [];
                $field.data('sync.triggers', triggers);
            } else {
                for (var i=0,m=triggers.length; i<m; i++) {
                    if (triggers[i] == type) {
                        bind = false;
                        break;
                    }
                }
            }
            if (bind) {
                triggers.push(type);
                S.bind($field, type);
            }
        },
        bind: function($field, type) {
            $field.bind(type+".sync", function() {
                $field.trigger('set.values', [$field.attr('name')]);
            });
        }
    };

    $.fn.sync = function(source, options, bidi) {
        options = $.extend({}, S.defaults, options);
        if (bidi !== undefined) options.bidi = bidi;
        var $source = source.jquery ? source : $(source);

        this.values($source);
        S.sync(this, $source, options);
        if (options.bidi) {
            S.sync($source, this, options);
        }
        return this;
    };

    $.fn.syncTo = function(source, options) {
        (source.jquery ? source : $(source)).sync(this, options, false)
        return this;
    }
    $.fn.syncFrom = function(source, options) {
        return this.sync(source, options, false);
    }

})(jQuery);
